/**
 * 
 */
package cn.kuaipai.weibo.http;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import cn.kuaipai.utils.Base64Encoder;
import cn.kuaipai.utils.StringTools;
import cn.kuaipai.weibo.constants.HttpMethod;
import cn.kuaipai.weibo.constants.WeiboConstants;
import cn.kuaipai.weibo.model.OAuth;
import cn.kuaipai.weibo.model.WeiboParameter;


/**
 * <B>授权服务提供</B>
 * 
 * @author zy
 */
public class OAuthProvider {
	protected final static Logger logger = Logger.getLogger(OAuthProvider.class);
	
	/**
	 * base string 算法
	 */
	private static final String hashAlgorithmName = "HmacSHA1";
	private static final WeiboHttpClient weiboClient = new WeiboHttpClient();

	public OAuth requestToken(OAuth oauth,String requestTokenURL){
		try{
			String queryString = constructionOauthParameters(requestTokenURL, HttpMethod.GET.getValue(),oauth.getOauthConsumerSecret(), null, oauth.getRequestTokenParameters());
			logger.info("[weibo] requestToken queryString:" + queryString);
			String response = weiboClient.httpGet(requestTokenURL,queryString);
			logger.info("[weibo] requestToken response:" + response );
			if (!isAuthorizated(oauth,response)) {
				oauth.setOauthStatus(WeiboConstants.RESULTCODE_REQUESTTOKEN_FAILURE);//WeiboResponseConstants.RESULTCODE_REQUESTTOKEN_FAILURE);
				logger.info("[weibo] requestToken error ! queryString = " + queryString + " response:" + response );
			}
			logger.info("[weibo] requestToken queryString:" + queryString + " oauth:" + oauth );
		}catch (Exception e) {
			logger.error("[weibo] requestToken error!" + oauth,e);
		}
		return oauth;
	}
	public String constructAuthorizeURL(String authorizeURL, String oauthToken){
		return authorizeURL.endsWith("?") ? authorizeURL + "oauth_token=" +  oauthToken : authorizeURL + "?oauth_token=" +  oauthToken;
	}

	public OAuth accessToken(OAuth oauth, String accessTokenURL){
		try{
			String queryString = constructionOauthParameters(accessTokenURL, HttpMethod.GET.getValue(),oauth.getOauthConsumerSecret(),oauth.getOauthTokenSecret(), oauth.getAccessTokenParameters());
			logger.info("[weibo] accessToken queryString:" + queryString);
			String response = weiboClient.httpGet(accessTokenURL, queryString);
			logger.info("[weibo] accessToken  response:" + response );
			if (!isAuthorizated(oauth,response)) {
				oauth.setOauthStatus(WeiboConstants.RESULTCODE_ACCESSTOKEN_FAILURE);
				logger.info("[weibo] accessToken error ! queryString = " + queryString + " response:" + response );
			}
			logger.info("[weibo] accessToken queryString:" + queryString + " oauth:" + oauth );
		}catch (Exception e) {
			logger.error("[weibo] accessToken error!" + oauth,e);
		}
		return oauth;
	}
	/**
	 * 构造请求参数
	 * @param url
	 * @param httpMethod
	 * @param consumerSecret
	 * @param tokenSecrect
	 * @param parameters
	 * @return
	 */
	public String constructionOauthParameters(String url, String httpMethod, String consumerSecret, String tokenSecrect,
			List<WeiboParameter> parameters) {
		//先对参数进行排序
		Collections.sort(parameters);
		String urlWithParameters = url;
		String parameterString = encodeParameters(parameters);
		if(StringUtils.isNotBlank(parameterString))
			urlWithParameters += "?" + parameterString ;
		URL aUrl = null;
		try {
			aUrl = new URL(urlWithParameters);
		} catch (MalformedURLException e) {
			System.err.println("URL parse error:" + e.getLocalizedMessage());
		}
		String signature = this.generateSignature(aUrl, consumerSecret, tokenSecrect, httpMethod, parameters);
		parameterString += "&oauth_signature=";
		parameterString += StringTools.encode(signature);

		return parameterString;
	}
	/**
	 * 构造OPENID请求字符串，必须有openid openkey(也就是说，必须经过授权验证)
	 * 
	 * @param url
	 * @param httpMethod
	 * @param openid
	 * @param openkey
	 * @param parameters
	 * @return
	 */
	public String constructionOpenIdKeyParameters(String url ,String httpMethod,String openid,String openkey,List<WeiboParameter> parameters){
		String parameterString = null ;
		try {
			parameters = new OAuth().getOpenIdAccessParameters();
			parameters.add(new WeiboParameter("openid", openid));
			parameters.add(new WeiboParameter("openkey", openkey));
			//参数排序
			Collections.sort(parameters);
			String urlWithParameters = url;
			parameterString = encodeParameters(parameters);
			if(StringUtils.isNotBlank(parameterString))
				urlWithParameters += "?" + parameterString ;
			URL aUrl = new URL(urlWithParameters);
			String base = this.generateSignatureBase(aUrl, httpMethod, parameters);
			Mac mac = Mac.getInstance(hashAlgorithmName);
			String oauthSignature = base + "&" + WeiboConstants.APP_SECRET ;
			SecretKeySpec spec = new SecretKeySpec(oauthSignature.getBytes(),hashAlgorithmName);
			mac.init(spec);
			byte[] bytes = mac.doFinal(base.getBytes());
			String sig =  new String(Base64Encoder.encode(bytes));
			parameterString += "&sig=";
			parameterString += StringTools.encode(sig);
		} catch (MalformedURLException e) {
			System.err.println("URL parse error:" + e.getLocalizedMessage());
		} catch (Exception e) {
			System.err.println(e);
		}
		return parameterString ;
	}
	/**
	 * <p>生成签名值</p>
	 * 
	 * <p>请求签名说明:</br>
	 *       所有TOKEN请求和受保护的资源请求必须被签名，微博开放平台会根据签名来判断请求的合法性。</br>
	 * 	  签名算法使用Signature Base String和密钥（Secret）生成签名，参数oauth_signature用于指定签名。</br>
	 * </p>
	 * <p>Signature Base String由以下三部分组成，各项之间使用&符号分隔。</br>
	 * 内容详见：http://wiki.open.t.qq.com/index.php/OAuth%E6%8E%88%E6%9D%83%E8%AF%B4%E6%98%8E</br>
	 * 
	 * 		<li>1、Http Method 请求方法，GET/POST</li>
	 * 		<li>2、URL Encode之后的请求URL（URL要小写）</li>
	 * 		<li>3、URL Encode并排序之后的请求参数</li>
	 * </p>
	 * @param url
	 * @param consumerSecret
	 * @param accessTokenSecret
	 * @param httpMethod
	 * @param parameters
	 * @return
	 */
	private String generateSignature(URL url, String consumerSecret, String accessTokenSecret, String httpMethod,
			List<WeiboParameter> parameters) {
		String base = this.generateSignatureBase(url, httpMethod, parameters);
		return this.generateSignature(base, consumerSecret, accessTokenSecret);
	}
	
	/**
	 * 生成字符串
	 * httpMethod&
	 * encode(url)&
	 * encode(parameters)
	 * 
	 * @param url
	 * @param httpMethod
	 * @param parameters
	 * @return
	 */
	private String generateSignatureBase(URL url, String httpMethod,List<WeiboParameter> parameters) {
		StringBuilder base = new StringBuilder();
		base.append(httpMethod.toUpperCase());
		base.append("&");
		base.append(StringTools.encode(getNormalizedUrl(url)));
		base.append("&");
		base.append(StringTools.encode(encodeParameters(parameters)));

		return base.toString();
	}
	private String generateSignature(String base, String consumerSecret, String accessTokenSecret) {
		try {
			Mac mac = Mac.getInstance(hashAlgorithmName);
			String oauthSignature = StringTools.encode(consumerSecret)
					+ "&"
					+ (StringUtils.isBlank(accessTokenSecret) ? "" : StringTools.encode(accessTokenSecret)); // 第三步accessToken使用
			SecretKeySpec spec = new SecretKeySpec(oauthSignature.getBytes(),
					hashAlgorithmName);
			mac.init(spec);
			byte[] bytes = mac.doFinal(base.getBytes());
			return new String(Base64Encoder.encode(bytes));
		} catch (Exception e) {
		}
		return null;
	}
	/**
	 * 整理URL
	 * @param url
	 * @return
	 */
	private static String getNormalizedUrl(URL url) {
		try {
			StringBuilder bufferURL = new StringBuilder();
			bufferURL.append(url.getProtocol());
			bufferURL.append("://");
			bufferURL.append(url.getHost());
			if ((url.getProtocol().equals("http") || url.getProtocol().equals("https")) 
					&& url.getPort() != -1) {
				bufferURL.append(":");
				bufferURL.append(url.getPort());
			}
			bufferURL.append(url.getPath());
			return bufferURL.toString();
		} catch (Exception e) {
			logger.error("[weibo] 整理URL路径 " + url.toString());
		}
		return null;
	}
	/**
	 * 对参数进行编码，包含参数名和参数值
	 * 
	 * @param parameters WeiboParameter
	 * @return String
	 */
	private static String encodeParameters(List<WeiboParameter> parameters) {
		StringBuilder result = new StringBuilder();
		for (WeiboParameter parameter : parameters) {
			if (result.length() > 0 ) {
				result.append("&");
			}
			result.append(StringTools.encode(parameter.getName()));
			result.append("=");
			result.append(StringTools.encode(parameter.getValue()));
		}
		return result.toString();
	}
	
	/**
	 * <p>授权是否通过</br>
	 * 返回数值如下：</br>
	 * oauth_token=39612a5f67ee423983f86fcb53207a27&oauth_token_secret=5838151ff88c4dc22925a301ac766904&oauth_callback_confirmed=true</p>
	 * </p>
	 * @param oath
	 * @param response
	 * @return 是否授权通过 true:是 false:否
	 */
	private boolean isAuthorizated(OAuth oauth,String response){
		if(StringUtils.isBlank(response) && StringUtils.indexOf(response,"&") < 0){
			return false ;
		}
		String result [] = StringUtils.split(response,"&");
		if(result == null){
			return false ;
		}
		
		String oauthToken = result[0];
		if(StringUtils.indexOf(oauthToken,"=") < 0){
			return false ;
		}
		oauth.setOauthToken(StringUtils.split(oauthToken, "=")[1]);
		
		String oauthTokenSecret = result[1];
		if(StringUtils.indexOf(oauthTokenSecret,"=") < 0){
			return false ;
		}
		oauth.setOauthTokenSecret(StringUtils.split(oauthTokenSecret,"=")[1]);
		return true ;
	}
	
	public static void main(String[] args) {
		OAuthProvider provider = new OAuthProvider();
		List<WeiboParameter> parameters = new ArrayList<WeiboParameter>();
		parameters.add(new WeiboParameter("format",WeiboConstants.FORMAT_RESPONSE_JSON));
		String openidAccessURL = provider.constructionOpenIdKeyParameters("http://open.t.qq.com/api/user/info", HttpMethod.GET.getValue(), "FB735A47F5C7C0E03327DE239CF4B942", "C0C89BAD4E2E7E07F0F50D6CC8351CF0", parameters);
		System.out.println(openidAccessURL);
	}
}
